<!-- eslint-disable no-console -->
<script lang="ts">
import { defineComponent, onMounted, reactive, computed, onUpdated, ref, nextTick } from 'vue';
import { imgSrcList, imgInfoList, imgListType, predictHeight } from '@/modules/img.enum';
import { positionType } from '@/modules/position.enum';
import * as _ from 'lodash';
import '@/assets/style/wave.scss';

export default defineComponent({
  name: 'WelcomeHome',
  setup() {
    // 所有数据的列表
    const allList: any = reactive({ value: [] });
    // 所有数据的位置
    const positions: any = reactive({ value: [] });
    // 滚动框的wrapper$el
    const virtualWrapper = ref();
    // 图片盒子容器
    const imgBox = ref();
    // 假div占用空间撑起滚动条用
    const occupyPos = ref();
    // 盒子高度
    const boxHeight = ref(0);
    // 盒子可见元素数量
    const visibleCount = ref(0);
    // 开始索引
    const start = ref(0);
    // 结束索引
    const end = ref(0);
    // 展示的dom节点
    const boxElLists: any = reactive({ value: {} });

    // 初始化位置
    const initPos = () => {
      positions.value = allList.value.map((item: any, index: number) => {
        return {
          index,
          height: predictHeight,
          top: index * predictHeight,
          bottom: (index + 1) * predictHeight,
        };
      });
    };

    const handleScroll = (e: any) => {
      const scrollY = Math.ceil(e.target.scrollTop);
      start.value = getScrollDataIndex(positions.value, scrollY);
      end.value = start.value + visibleCount.value;
      setContainScrollTop();
    };

    // 二分查找准确的scrollTop位置
    // list为数据总数据, scrollTop为滚动条滚动高度
    const getScrollDataIndex = (list: positionType[], scrollTop: Number): any => {
      let start = 0;
      let end = list.length - 1;
      let tempIndex = null;
      while (start <= end) {
        const middle = Math.floor((start + end) / 2);
        const middleValue = list[middle].bottom;
        if (middleValue === scrollTop) {
          // 因为是以bottom作为参照，返回的是列表开始索引，需要+1
          return middle + 1;
        } else if (middleValue < scrollTop) {
          start = middle + 1;
        } else {
          if (tempIndex === null || tempIndex > middle) {
            tempIndex = middle;
          }
          end = end - 1;
        }
      }
      return tempIndex;
    };

    // 动态生成数据
    const generateList = (): imgListType[] => {
      const allList: imgListType[] = [];
      const { length: srcLength } = imgSrcList;
      const { length: infoLength } = imgInfoList;
      for (let i = 0; i < 1000; i++) {
        const srcRandom = Math.floor(Math.random() * srcLength);
        const infoRandom = Math.floor(Math.random() * infoLength);
        const item: imgListType = {
          id: i,
          imgSrc: imgSrcList[srcRandom],
          imgInfo: imgInfoList[infoRandom],
        };
        allList.push(item);
      }
      return allList;
    };

    // 更新所有数据位置
    const updatePosition = () => {
      let itemIdx = 0;
      let diffH = 0;
      const elList = document.querySelectorAll('.img-wrapper');
      elList.forEach((item, index) => {
        const itemData = item;
        const { height: curHeight } = itemData.getBoundingClientRect();

        itemIdx = +item.id;
        const oldHeight = positions.value[itemIdx].height;
        diffH = oldHeight - curHeight;
        if (curHeight !== oldHeight) {
          positions.value[itemIdx].bottom = positions.value[itemIdx].bottom - diffH;
          positions.value[itemIdx].height = curHeight;
          for (let k = itemIdx + 1; k < positions.value.length; k++) {
            positions.value[k].top = positions.value[k - 1].bottom;
            positions.value[k].bottom = positions.value[k].bottom - diffH;
          }
        }
      });

      console.log('最后的位置', positions.value);
      occupyPos.value.style.height = `${positions.value[positions.value.length - 1].bottom}px`;
      console.log('整个div高度', occupyPos.value.style.height);
    };

    const getBoxDoms = (el: any) => {
      el && (boxElLists.value[el.id] = el);
    };

    // 设置容器距顶距离
    const setContainScrollTop = () => {
      const top = start.value < 1 ? 0 : positions.value[start.value - 1].bottom;
      imgBox.value.style.transform = `translate3d(0,${top}px,0)`;
    };

    // 初始化下标以及高度等信息
    const initIndex = () => {
      start.value = 0;
      end.value = visibleCount.value + start.value;
    };

    const resizeObserver = new ResizeObserver((entries: any) => {
      entries.forEach((e: any) => {
        const {
          contentRect: { height },
          target,
        } = e;
        const { id } = target;
        positions.value[id].height = height;
      });
    });

    onMounted(() => {
      boxHeight.value = virtualWrapper.value.clientHeight;
      allList.value = generateList();
      console.log('正确答案', allList.value);
      initPos();
      getVisibleCount();
      initIndex();
      console.log('onMounted');
    });

    onUpdated(async () => {
      console.log('onUpdated');
      await nextTick();
      if (Object.keys(boxElLists.value).length === 0) {
        return;
      }
      resizeObserver.disconnect();
      Object.keys(boxElLists.value).forEach((item) => {
        resizeObserver.observe(boxElLists.value[item]);
      });
      updatePosition();
    });

    // 可容纳数据量
    const getVisibleCount = () => {
      visibleCount.value = Math.ceil(boxHeight.value / predictHeight);
    };

    // 可容纳列表
    const visibleData = computed(() => {
      return allList.value.slice(start.value, end.value);
    });

    // 动态高度
    const wrapperHeight = computed(() => {
      const {
        value,
        value: { length },
      } = positions;
      return length === 0 ? 0 : value[length - 1].bottom;
    });

    return {
      allList,
      positions,
      wrapperHeight,
      visibleData,
      virtualWrapper,
      imgBox,
      occupyPos,
      getBoxDoms,
      visibleCount,
      handleScroll,
    };
  },
});
</script>

<template>
  <main class="virtual-main">
    <div ref="virtualWrapper" class="virtual-wrapper" @scroll="handleScroll">
      <!-- <div class="occupied-position" :style="{ height: wrapperHeight + 'px' }"></div> -->
      <div ref="occupyPos" class="occupied-position"></div>
      <div ref="imgBox">
        <div
          v-for="item in visibleData"
          :id="`${item.id}`"
          :key="item.id"
          :ref="getBoxDoms"
          class="img-wrapper"
        >
          <img :src="item.imgSrc" alt="not found" />
          {{ item.imgInfo }}
        </div>
      </div>
    </div>
    <!-- <div class="wave"> -->
    <div class="wave-wrapper">
      <svg
        id="svg"
        width="100%"
        height="100%"
        viewBox="0 0 1440 600"
        xmlns="http://www.w3.org/2000/svg"
        class="transition duration-300 ease-in-out delay-150 wave"
      >
        <path
          d="M 0,600 C 0,600 0,200 0,200 C 74.74641148325358,184.555023923445 149.49282296650716,169.11004784688996 263,173 C 376.50717703349284,176.88995215311004 528.775119617225,200.11483253588517 616,223 C 703.224880382775,245.88516746411483 725.4066985645932,268.4306220095694 812,253 C 898.5933014354068,237.5693779904306 1049.598086124402,184.16267942583733 1165,169 C 1280.401913875598,153.83732057416267 1360.200956937799,176.91866028708134 1440,200 C 1440,200 1440,600 1440,600 Z"
          stroke="none"
          stroke-width="0"
          fill="#ff008088"
          class="transition-all duration-300 ease-in-out delay-150 path-0"
        ></path>

        <path
          d="M 0,600 C 0,600 0,400 0,400 C 66.68899521531102,360.72727272727275 133.37799043062205,321.45454545454544 241,344 C 348.62200956937795,366.54545454545456 497.17703349282294,450.9090909090909 611,463 C 724.8229665071771,475.0909090909091 803.9138755980862,414.9090909090909 879,400 C 954.0861244019138,385.0909090909091 1025.1674641148325,415.45454545454544 1118,423 C 1210.8325358851675,430.54545454545456 1325.4162679425838,415.27272727272725 1440,400 C 1440,400 1440,600 1440,600 Z"
          stroke="none"
          stroke-width="0"
          fill="#ff0080ff"
          class="transition-all duration-300 ease-in-out delay-150 path-1"
        ></path>
      </svg>
    </div>
    <!-- </div> -->
  </main>
</template>

<style lang="scss" scoped>
.occupied-position {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  z-index: -1;
}
.virtual-main {
  width: 100%;
  height: 100%;

  .virtual-wrapper {
    position: relative;
    margin: 0 auto;
    margin-top: 10vh;
    width: 1000px;
    height: 650px;
    overflow: auto;
    border-top-left-radius: 50px;
    border-bottom-left-radius: 50px;
    background: #e0e0e0;
    box-shadow: 20px 20px 60px #bebebe, -20px -20px 60px #ffffff;
    padding: 20px;
    .img-wrapper {
      display: flex;
      flex-direction: column;
      align-items: center;
      font-size: 24px;
      > img {
        width: 300px;
        margin-bottom: 5px;
      }
    }
  }
}
.wave {
  position: fixed;
  left: 0px;
  width: 100%;
  overflow: hidden;
  &-wrapper {
    margin-top: -300px;
  }
}
</style>
